package com.bstek.uflo.console.controller;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.web.servlet.ModelAndView;

import com.bstek.dorado.util.CloneUtils;
import com.bstek.dorado.web.resolver.AbstractResolver;
import com.bstek.uflo.diagram.NodeDiagram;
import com.bstek.uflo.diagram.ProcessDiagram;
import com.bstek.uflo.diagram.SequenceFlowDiagram;
import com.bstek.uflo.diagram.TaskDiagramInfoProvider;
import com.bstek.uflo.diagram.TaskInfo;
import com.bstek.uflo.model.HistoryActivity;
import com.bstek.uflo.model.HistoryTask;
import com.bstek.uflo.model.ProcessDefinition;
import com.bstek.uflo.model.ProcessInstance;
import com.bstek.uflo.model.task.Task;
import com.bstek.uflo.process.node.Node;
import com.bstek.uflo.process.node.StartNode;
import com.bstek.uflo.process.node.TaskNode;
import com.bstek.uflo.query.HistoryTaskQuery;
import com.bstek.uflo.query.TaskQuery;
import com.bstek.uflo.service.HistoryService;
import com.bstek.uflo.service.ProcessService;
import com.bstek.uflo.service.TaskService;

/**
 * @author Jacky.gao
 * @since 2013年9月9日
 */
public class ProcessDiagramController extends AbstractResolver implements InitializingBean{
	private ProcessService processService;
	private TaskService taskService;
	private HistoryService historyService;
	private List<TaskDiagramInfoProvider> providers;
	@Override
	protected ModelAndView doHandleRequest(HttpServletRequest req,HttpServletResponse res) throws Exception {
		String taskId,processInstanceId,processId,processKey;
		ProcessDiagram diagram=null;
		taskId=req.getParameter("taskId");
		processInstanceId=req.getParameter("processInstanceId");
		processId=req.getParameter("processId");
		processKey=req.getParameter("processKey");
		if(StringUtils.isNotEmpty(taskId)){
			Task task=taskService.getTask(Long.valueOf(taskId));
			if(task==null){
				throw new IllegalArgumentException("Task "+taskId+" is not exist!");
			}
			processInstanceId=String.valueOf(task.getProcessInstanceId());
			diagram=buildDiagramByTaskId(task);
		}else if(StringUtils.isNotEmpty(processInstanceId)){
			diagram=buildDiagramByProcessInstanceId(processInstanceId);
		}else if(StringUtils.isNotEmpty(processId)){
			diagram=buildDiagramByProcessId(processId);
		}else if(StringUtils.isNotEmpty(processKey)){
			diagram=buildDiagramByProcessKey(processKey);
		}
		if(diagram==null){
			throw new IllegalArgumentException("There is not enough information to load process diagram!");
		}
		if(StringUtils.isNotEmpty(processInstanceId) && !processInstanceId.equals("0")){
			ProcessInstance pi=processService.getProcessInstanceById(Long.valueOf(processInstanceId));
			ProcessDefinition pd=processService.getProcessById(pi.getProcessId());
			List<NodeDiagram> nodeDiagrams=diagram.getNodeDiagrams();
			for(NodeDiagram nodeDiagram:nodeDiagrams){
				Node node=pd.getNode(nodeDiagram.getName());
				if(!(node instanceof TaskNode) && !(node instanceof StartNode)){
					continue;
				}
				TaskQuery query=taskService.createTaskQuery();
				query.rootProcessInstanceId(pi.getRootId());
				query.nodeName(node.getName());
				List<Task> tasks=query.list();
				StringBuffer sb=null;
				for(TaskDiagramInfoProvider provider:providers){
					String info=null;
					List<TaskInfo> taskInfoList=new ArrayList<TaskInfo>();
					if(tasks.size()>0){
						taskInfoList.addAll(buildTaskInfos(tasks));
					}
					HistoryTaskQuery historyTaskQuery=historyService.createHistoryTaskQuery();
					historyTaskQuery.rootProcessInstanceId(pi.getRootId());
					historyTaskQuery.nodeName(node.getName());
					List<HistoryTask> historyTasks=historyTaskQuery.list();
					if(historyTasks.size()>0){
						taskInfoList.addAll(buildHistoryTaskInfos(historyTasks));						
					}
					info=provider.getInfo(node.getName(),taskInfoList);
					if(StringUtils.isNotEmpty(info)){
						if(sb==null){
							sb=new StringBuffer();
						}
						sb.append(info);
						sb.append("<br>");
					}
				}
				if(sb!=null){
					nodeDiagram.setInfo(sb.toString());
				}
			}
		}
		res.setContentType("text/json");  
		res.setCharacterEncoding("UTF-8");  
		ObjectMapper mapper=new ObjectMapper();
		OutputStream out=res.getOutputStream();
		try{
			mapper.writeValue(out, diagram);			
		}finally{
			out.flush();
			out.close();
		}
		return null;
	}
	
	private List<TaskInfo> buildTaskInfos(List<Task> tasks){
		List<TaskInfo> infos=new ArrayList<TaskInfo>();
		for(Task task:tasks){
			TaskInfo info=new TaskInfo();
			info.setAssignee(task.getAssignee());
			info.setBusinessId(task.getBusinessId());
			info.setCreateDate(task.getCreateDate());
			info.setDescription(task.getDescription());
			info.setDuedate(task.getDuedate());
			info.setOpinion(task.getOpinion());
			info.setOwner(task.getOwner());
			info.setProcessId(task.getProcessId());
			info.setProcessInstanceId(task.getProcessInstanceId());
			info.setState(task.getState());
			info.setTaskId(task.getId());
			info.setTaskName(task.getTaskName());
			info.setTaskId(task.getId());
			info.setType(task.getType());
			info.setUrl(task.getUrl());
			infos.add(info);
		}
		return infos;
	}
	private List<TaskInfo> buildHistoryTaskInfos(List<HistoryTask> tasks){
		List<TaskInfo> infos=new ArrayList<TaskInfo>();
		for(HistoryTask task:tasks){
			TaskInfo info=new TaskInfo();
			info.setAssignee(task.getAssignee());
			info.setBusinessId(task.getBusinessId());
			info.setCreateDate(task.getCreateDate());
			info.setDescription(task.getDescription());
			info.setDuedate(task.getDuedate());
			info.setEndDate(task.getEndDate());					
			info.setOpinion(task.getOpinion());
			info.setOwner(task.getOwner());
			info.setProcessId(task.getProcessId());
			info.setProcessInstanceId(task.getProcessInstanceId());
			info.setState(task.getState());
			info.setTaskId(task.getId());
			info.setTaskName(task.getTaskName());
			info.setTaskId(task.getId());
			info.setType(task.getType());
			info.setUrl(task.getUrl());
			infos.add(info);
		}
		return infos;
	}
	
	private ProcessDiagram buildDiagramByTaskId(Task task){
		ProcessInstance pi=processService.getProcessInstanceById(task.getProcessInstanceId());
		ProcessDefinition pd=processService.getProcessById(task.getProcessId());
		ProcessDiagram diagram=pd.getDiagram();
		try {
			diagram=CloneUtils.clone(diagram);
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e);
		}
		rebuildProcessDiagram(diagram, pi.getRootId());
		for(NodeDiagram nodeDiagram:diagram.getNodeDiagrams()){
			if(nodeDiagram.getName().equals(task.getNodeName())){
				nodeDiagram.setBackgroundColor("255,255,255");
				nodeDiagram.setBorderColor("0,160,233");
				nodeDiagram.setFontColor("0,160,233");
				nodeDiagram.setFontBold(true);
				break;
			}
		}
		return diagram;
	}
	
	private ProcessDiagram buildDiagramByProcessInstanceId(String processInstanceId){
		ProcessInstance pi=processService.getProcessInstanceById(Long.valueOf(processInstanceId));
		if(pi==null){
			throw new IllegalArgumentException("ProcessInstance "+processInstanceId+" is not exist!");
		}
		ProcessDefinition pd=processService.getProcessById(pi.getProcessId());
		ProcessDiagram diagram=pd.getDiagram();
		try {
			diagram=CloneUtils.clone(diagram);
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e);
		}
		rebuildProcessDiagram(diagram,pi.getRootId());
		return pd.getDiagram();
	}
	private ProcessDiagram buildDiagramByProcessId(String processId){
		ProcessDefinition pd=processService.getProcessById(Long.valueOf(processId));
		return pd.getDiagram();
	}
	private ProcessDiagram buildDiagramByProcessKey(String processKey){
		ProcessDefinition pd=processService.getProcessByKey(processKey);
		return pd.getDiagram();
	}
		
	private void rebuildProcessDiagram(ProcessDiagram diagram,long processInstanceId) {
		Map<String,Integer> countMap=new HashMap<String,Integer>();
		Map<String,HistoryActivity> map=new HashMap<String,HistoryActivity>();
		List<HistoryActivity> activities=historyService.getHistoryActivitysByProcesssInstanceId(processInstanceId);
		for(HistoryActivity hisActivity:activities){
			String nodeName=hisActivity.getNodeName();
			if(countMap.containsKey(nodeName)){
				int count=countMap.get(nodeName);
				count++;
				countMap.put(nodeName, count);
			}else{
				countMap.put(nodeName, 1);				
			}
		}
		for(NodeDiagram d:diagram.getNodeDiagrams()){
			String nodeName=d.getName();
			HistoryActivity activity=getHistoryActivity(activities,nodeName);
			if(activity==null)continue;
			map.put(nodeName, activity);
			if(countMap.containsKey(nodeName)){
				int count=countMap.get(nodeName);
				d.setTime(count);
			}
			String icon=d.getIcon();
			String resultIcon=null;
			if(activity.getEndDate()==null){
				resultIcon=icon.substring(0,icon.lastIndexOf("/"))+"/current"+icon.substring(icon.lastIndexOf("/"));
				d.setFontColor("76,177,255");
				d.setBackgroundColor("250,250,250");
				d.setBorderColor("76,121,156");
				d.setFontColor("18,42,136");
			}else{
				resultIcon=icon.substring(0,icon.lastIndexOf("/"))+"/passed"+icon.substring(icon.lastIndexOf("/"));
				d.setFontColor("150,150,150");
				d.setBorderColor("200,200,200");
				d.setBackgroundColor("245,245,245");
			}
			d.setIcon(resultIcon);
		}
		
		for(NodeDiagram d:diagram.getNodeDiagrams()){
			String sourceName=d.getName();
			HistoryActivity source=map.get(sourceName);
			if(source==null || d.getSequenceFlowDiagrams()==null)continue;
			for(SequenceFlowDiagram flowDiagram:d.getSequenceFlowDiagrams()){
				HistoryActivity target=map.get(flowDiagram.getTo());
				if(target==null){
					continue;
				}
				/*String leaveFlowName=source.getLeaveFlowName();
				if(leaveFlowName==null || leaveFlowName.equals(flowDiagram.getName())){
					flowDiagram.setBorderColor("200,200,200");
					flowDiagram.setFontColor("150,150,150");
				}*/
				
				flowDiagram.setBorderColor("200,200,200");
				flowDiagram.setFontColor("150,150,150");
			}
		}
	}

	
	private HistoryActivity getHistoryActivity(List<HistoryActivity> activities,String nodeName){
		List<HistoryActivity> result=new ArrayList<HistoryActivity>();
		for(HistoryActivity activity:activities){
			if(activity.getNodeName().equals(nodeName)){
				result.add(activity);				
			}
		}
		HistoryActivity target=null;
		for(HistoryActivity ac:result){
			if(ac.getEndDate()==null){
				target=ac;
				break;
			}
		}
		if(target==null && result.size()>0){
			return result.get(0);
		}else{
			return target;			
		}
	}
	
	public void afterPropertiesSet() throws Exception {
		providers=new ArrayList<TaskDiagramInfoProvider>();
		Collection<TaskDiagramInfoProvider> parentProviders=getApplicationContext().getBeansOfType(TaskDiagramInfoProvider.class).values();
		for(TaskDiagramInfoProvider provider:parentProviders){
			if(!provider.disable()){
				providers.add(provider);
			}
		}
		ApplicationContext context=getApplicationContext().getParent();
		if(context==null)return;
		for(TaskDiagramInfoProvider provider:context.getBeansOfType(TaskDiagramInfoProvider.class).values()){
			if(!provider.disable()){
				providers.add(provider);
			}
		}
	}
	
	public void setProcessService(ProcessService processService) {
		this.processService = processService;
	}

	public void setTaskService(TaskService taskService) {
		this.taskService = taskService;
	}
	public void setHistoryService(HistoryService historyService) {
		this.historyService = historyService;
	}
}
